package org.liaohailong.library.widget

import android.content.Context
import android.graphics.Point
import android.graphics.SurfaceTexture
import android.media.MediaPlayer
import android.util.AttributeSet
import android.util.Log
import android.view.Surface
import android.view.TextureView

/**
 * Author: liaohailong
 * Date: 2019/3/16
 * Time: 22:19
 * Description: 根据视频大小包裹自身的VideoView 父容器最好是FrameLayout
 **/
class WrapContentVideoView(context: Context, attributeSet: AttributeSet?, defStyle: Int) :
        TextureView(context, attributeSet, defStyle),
        MediaPlayer.OnPreparedListener,
        MediaPlayer.OnCompletionListener,
        MediaPlayer.OnErrorListener,
        MediaPlayer.OnVideoSizeChangedListener {
    constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)
    constructor(context: Context) : this(context, null)

    private var videoWidth = 0
    private var videoHeight = 0

    private val playObserver: MutableSet<OnTextureVideoViewCallback> = mutableSetOf()
    private var mediaPlayer: MediaPlayer? = null
    private var isMediaPlayerSetSurface = false
    private var isFirstMediaPrepared = false
    private var surface: Surface? = null

    private var seekTime = 0

    init {
        surfaceTextureListener = object : SurfaceTextureListener {
            override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {
                this@WrapContentVideoView.surface = Surface(surface)
                isMediaPlayerSetSurface = false
                setMediaPlayerWithSurface()
            }

            override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) {
                this@WrapContentVideoView.surface = Surface(surface)
                isMediaPlayerSetSurface = false
                setMediaPlayerWithSurface()
            }

            override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {

            }

            override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean {
                this@WrapContentVideoView.surface?.release()
                surface?.release()
                this@WrapContentVideoView.surface = null
                mediaPlayer?.setSurface(null)
                isMediaPlayerSetSurface = false
                return true
            }
        }
    }

    fun addPlayObserver(callback: OnTextureVideoViewCallback) {
        playObserver.add(callback)
    }

    fun removePlayObserver(callback: OnTextureVideoViewCallback) {
        playObserver.remove(callback)
    }

    fun seekForStart(seek: Int) {
        seekTime = seek
    }

    fun play(url: String) {
        if (mediaPlayer == null) {
            mediaPlayer = MediaPlayer()
            mediaPlayer!!.setOnPreparedListener(this@WrapContentVideoView)
            mediaPlayer!!.setOnCompletionListener(this@WrapContentVideoView)
            mediaPlayer!!.setOnErrorListener(this@WrapContentVideoView)
            mediaPlayer!!.setOnVideoSizeChangedListener(this@WrapContentVideoView)
        }
        mediaPlayer?.apply {
            try {
                if (isPlaying) stop()
                reset()
                setDataSource(url)
                if (url.startsWith("http", true)
                        || url.startsWith("rtsp", true)
                        || url.startsWith("rtmp", true)) {
                    prepareAsync()
                } else prepare()
            } catch (e: Exception) {
                Log.i("WrapContentVideoView", "play 失败 e = $e")
                onError(mediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, MediaPlayer.MEDIA_ERROR_IO)
            }
        }
    }

    fun getCurrentPosition(): Long {
        return if (mediaPlayer == null) 0 else mediaPlayer!!.currentPosition.toLong()
    }

    fun setMute(mute: Boolean) {
        if (mute) {
            mediaPlayer?.setVolume(0F, 0F)
        } else {
            mediaPlayer?.setVolume(1F, 1F)
        }
    }

    private fun setMediaPlayerWithSurface() {
        if (!isFirstMediaPrepared) return
        if (mediaPlayer == null) return
        if (surface == null) return
        if (isMediaPlayerSetSurface) return
        mediaPlayer!!.setSurface(surface!!)
        isMediaPlayerSetSurface = true
    }

    private fun updateTextureViewSize(): Point {
        val point = Point()
        if (width <= 0 || height <= 0 || videoWidth <= 0 || videoHeight <= 0) return point

        val scaleX = width / videoWidth.toFloat()
        val scaleY = height / videoHeight.toFloat()
        val scale = Math.min(scaleX, scaleY)

        val targetWidth = videoWidth * scale
        val targetHeight = videoHeight * scale
        point.set(targetWidth.toInt(), targetHeight.toInt())

        val layoutParams = layoutParams
        if (layoutParams.width == targetWidth.toInt() || layoutParams.height == targetWidth.toInt()) return point

        layoutParams.width = targetWidth.toInt()
        layoutParams.height = targetHeight.toInt()
        setLayoutParams(layoutParams)

        point.set(layoutParams.width, layoutParams.height)
        return point
    }

    fun pause() {
        mediaPlayer?.apply {
            if (isPlaying) pause()
            seekTime = currentPosition
        }
    }

    fun resume() {
        mediaPlayer?.apply {
            if (seekTime > 0) {
                seekTo(seekTime)
                seekTime = 0
            }
            start()
        }
    }

    fun release() {
        mediaPlayer?.apply {
            if (isPlaying) stop()
            release()
            mediaPlayer = null
        }
    }

    override fun onPrepared(mp: MediaPlayer?) {
        isFirstMediaPrepared = true
        setMediaPlayerWithSurface()
        for (observer in playObserver) observer.onPrepared()
        if (seekTime > 0L) {
            mp?.seekTo(seekTime)
            seekTime = 0
        }
        mp?.start()
    }

    override fun onVideoSizeChanged(mp: MediaPlayer?, width: Int, height: Int) {
        videoWidth = width
        videoHeight = height
        val scaledSize = updateTextureViewSize()
        for (observer in playObserver) {
            observer.onScaledVideoSize(scaledSize.x, scaledSize.y)
        }
    }

    override fun onCompletion(mp: MediaPlayer?) {
        for (observer in playObserver) observer.onAutoComplete()
    }

    override fun onError(mp: MediaPlayer?, what: Int, extra: Int): Boolean {
        for (observer in playObserver) observer.onError(what, extra)
        return true
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        release()
    }

    interface OnTextureVideoViewCallback {
        fun onPrepared()
        fun onScaledVideoSize(width: Int, height: Int)
        fun onAutoComplete()
        fun onError(what: Int, extra: Int)
    }
}